#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#define LINE_NUM 1024
#define OPT_NUM 64

#define EXIT_INFO 0

#define NONE_REDIR   0 //默认重定向
#define INPUT_REDIR  1 //输入重定向
#define OUTPUT_REDIR 2 //输出重定向
#define APPEND_REDIR 3 //追加重定向
#define TRIMPSPACE(start) do{\
                                while(isspace(*start)) start++;\
                            }while(0)


//重定向标识
int redir_type = NONE_REDIR;
char* redir_file = NULL;

char line_command[LINE_NUM];//命令行
char* myargv[OPT_NUM];//选项

void check_redir(char* command)
{
    assert(command != NULL);
    char* start = command;
    char* end = command + strlen(command);
    while(start < end)
    {
        //输入重定向
        if(*start == '>')
        {
            // ls > log.txt
            // ls >> log.txt
            *start = '\0';
            ++start;
            if(*start == '>')
            {
                redir_type = APPEND_REDIR;
                start++;
            }
            else 
                redir_type = OUTPUT_REDIR;
            TRIMPSPACE(start);
            redir_file = start;
            break;
        }
        //输出重定向
        else if(*start == '<')
        {
            // cat < log.txt;
            redir_type = INPUT_REDIR;
            *start = '\0';
            start++;
            TRIMPSPACE(start);
            redir_file = start;
            break;
        }
        else 
        {
            ++start;
        }
    }
}

int exit_code = EXIT_INFO;
int exit_state = EXIT_INFO;

int main()
{
    while(1){
        //初始化重定向选择
        redir_file = NULL;
        redir_type = NONE_REDIR;

        //模拟界面
        printf("用户名@主机名 # "); 
        fflush(stdout);

        //命令行
        char * r = fgets(line_command, sizeof(line_command), stdin);
        assert(r != NULL);
        line_command[strlen(line_command)-1] = 0;
        int i = 1;
        
        //判断是否有 > >> < 重定向符号
        check_redir(line_command);
        
        myargv[0] = strtok(line_command, " ");

        
        //为ls上色 
        if(myargv[0] && !strcmp(myargv[0], "ls"))
        {
            myargv[i++] = "--color=auto";
        }

        //为ls -l取ll别名
        if(myargv[0] && !strcmp(myargv[0], "ll"))
        {
            myargv[0] = "ls";
            myargv[i++] = "-l";
            myargv[i++] = "--color=auto";
        }

        //拆分命令
        while(myargv[i++] = strtok(NULL, " "));
        
        //echo
        if(myargv[0] && !strcmp(myargv[0], "echo"))
        {
            if(myargv[1] && !strcmp(myargv[1],"$?"))
            {
                printf("exit_code: %d, exit_state: %d\n", exit_code, exit_state);
                continue;
            }
            //nothing
        }

        //将父进程chdir
        if(myargv[0] && myargv [1] && !strcmp(myargv[0],"cd"))
        {
            chdir(myargv[1]);
            continue;
        }
        
        
#if DEBUG 
        //printf(line_command);
       for(int j = 0; myargv[j] != NULL; j++)
       {
           printf("%d: %s\n", j, myargv[j]);
       }
       //printf(redir_file);
       //printf("%d\n",redir_type);
#endif
       //子进程执行进程替换，执行系统指令
       pid_t id = fork();
       //assert(id != 0);
       if(!id)
       {
           //child
           //重定向 
           switch(redir_type)
           { 
               case NONE_REDIR:
                   break;
               case INPUT_REDIR:
                   {
                       int fd = open(redir_file, O_RDONLY);
                       if(fd < 0)
                       {
                           perror("open");
                           exit(errno);//INPUT ERR 
                       }
                       dup2(fd, 0);
                   }
                   break;
               case OUTPUT_REDIR:
               case APPEND_REDIR:
                   {
                        int flags = O_WRONLY | O_CREAT;
                        if(redir_type == APPEND_REDIR)
                            flags |= O_APPEND;
                        else 
                            flags |= O_TRUNC;
                        int fd = open(redir_file, flags, 0666);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);//OUTPUT | APPEND ERR 
                        }
                        dup2(fd, 1);
                   }
                   break;
               default:
                   {
                       printf("bug!\n");
                   }
                   break;
          }
           execvp(myargv[0],myargv);
           exit(1);
       }
       int status = 0;
       waitpid(id, &status, 0);
       exit_code = status & (0x7F);
       exit_state = (status >> 8) & (0xFF);
    }
}
